package com.jch.demo.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Order(2)
@Aspect // 声明为切面
@Component // 放入 Spring AOP容器中
public class LoggingAspect {

    /**
     * 创建一个方法，用于声明切入点表达式。一般，方法中再不需要添入其他的代码
     * 使用@Pointcut注解声明切入点表达式
     * 其他的通知直接使用方法名来引用当前的切入点表达式
     */
    @Pointcut("execution(* com.jch.demo.aop.beans.ArithmeticCalculator.*(..))")
    public void declareJoinPointExpression(){}

    // 前置通知 无连接点
//    @Before("execution(public double com.jch.demo.aop.beans.ArithmeticCalculator.*(double,double))")
    @Before("declareJoinPointExpression()")
    public void beforeMethod(){
        System.out.println("before method");
    }

    // 前置通知 带有连接点 或传入参数
//    @Before("execution(public double com.jch.demo.aop.beans.ArithmeticCalculator.*(..))")
    @Before("declareJoinPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
        System.out.println("method name:" + joinPoint.getSignature().getName());
        System.out.println("method args:" + Arrays.toString(joinPoint.getArgs()));
        System.out.println();
    }

    // 在方法正常结束后 执行的代码
    // 返回通知 可以访问到方法的返回值
    @AfterReturning(value = "execution(* com.jch.demo.aop.beans.ArithmeticCalculator.*(..))",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        System.out.println("afterReturning method name:" + joinPoint.getSignature().getName());
        System.out.println("afterReturning method result:" + result);
        System.out.println();
    }

    // 在方法异常结束 执行的代码
    // 返回通知 可以访问到方法的异常，可以指定在出现特定异常时再执行通知代码
    @AfterThrowing(value = "execution(* com.jch.demo.aop.beans.ArithmeticCalculator.*(..))",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        System.out.println("afterThrowing method name:" + joinPoint.getSignature().getName());
        System.out.println("afterThrowing method throwing:" + e);
        System.out.println();
    }

    // 后置通知
    @After("execution(* com.jch.demo.aop.beans.ArithmeticCalculator.*(..))")
    public void afterMethod(){
        System.out.println("afterMethod");
        System.out.println("end method");
    }

    // 后置通知 带有连接点 或 传入参数
    @After("execution(* com.jch.demo.aop.beans.ArithmeticCalculator.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        System.out.println("afterMethod");
        System.out.println("method name:" + joinPoint.getSignature().getName());
        System.out.println();
    }

    // 环绕通知
    // 环绕通知 需要带有ProceedingJoinPoint类型的参数
    // 环绕通知类似于动态代理的全部过程：ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
    // 且环绕通知必须有返回值，返回值即为目标方法的返回值。
    @Around("execution(* com.jch.demo.aop.beans.ArithmeticCalculator.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        System.out.println("begin aroundMethod");
        Object result = null;
        try {
            // 前置通知
            System.out.println("aroundMethod before Method name:" + pjd.getSignature().getName() + " args:" + Arrays.asList(pjd.getArgs()));
            // 执行目标方法
            result = pjd.proceed();
            // 后置通知
            System.out.println("aroundMethod afterReturning method name:" + pjd.getSignature().getName() + " result:" + result);
        } catch (Throwable throwable) {
            // 异常通知
            System.out.println("aroundMethod afterThrowing method name:" + pjd.getSignature().getName() + " throwable:" + throwable);
        }
        // 后置通知
        System.out.println("aroundMethod end method name:" + pjd.getSignature().getName());
        System.out.println("end aroundMethod");
        return result;
    }
}
